home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 007 / lpmac1.arc / MACROS.SRC < prev    next >
Text File  |  1987-03-03  |  30KB  |  1,044 lines

  1. ;;
  2. ;;    This is file: MACROS.MLB.
  3. ;;
  4. ;;    It contains the source for a macro library.
  5. ;;    Written by Mark Hersey.
  6. ;;
  7. ;;    Comments by Lew Paper, 2/16/87
  8. ;;
  9. ;;    Print macro expansion control by Lew Paper
  10. ;;    Default is .XALL, which prints code only
  11. ;;    Change by macro variable MACRO_EXPANSION_CONTROL
  12. ;;    0 for .XALL, which prints code only
  13. ;;    1 for .SALL, which surpresses all printing
  14. ;;    2 for .LALL, which prints both code and comments
  15.     .XCREF    SET_MACRO_EXPANSION
  16. SET_MACRO_EXPANSION    MACRO
  17.      IFNDEF    MACRO_EXPANSION_CONTROL
  18.     .XALL
  19.     ENDIF                ;; IFNDEF MACRO_EXPANSION_CONTROL
  20.     IFDEF    MACRO_EXPANSION_CONTROL
  21.     IF     MACRO_EXPANSION_CONTROL EQ 1
  22.     .SALL
  23.     ELSE
  24.     IF    MACRO_EXPANSION_CONTROL EQ 2
  25.     .LALL
  26.     ENDIF                ;; IF MACRO_EXPANSION_CONTROL EQ 2
  27.     ENDIF                ;; IF MACRO_EXPANSION_CONTROL EQ 1
  28.     ENDIF                ;; IFDEF MACRO_EXPANSION_CONTROL
  29.     ENDM                ;; SET_MACRO_EXPANDSION
  30. ;;
  31. ;;    I8086 symbols repeated by LP
  32.         IFNDEF    TRUE
  33. TRUE        EQU    001H    ;Used to indicate true condition.
  34.         ENDIF        ;IFNDEF TRUE
  35.         IFNDEF    ZFLAG
  36. ZFLAG        EQU    00040H
  37.         ENDIF        ;IFNDEF ZFLAG
  38. ;;
  39. ;;        Everything from here to the end of macro POP_REGISTERS builds
  40. ;;    the macro pair PUSH_REGISTERS and POP_REGISTERS.  PUSH_REGISTERS
  41. ;;    pushes a list of registers and builds a stack for POP_REGISTERS
  42. ;;    to restore them from.
  43. ;;    LP
  44. ;;
  45. ;;    Convert a REGISTER to a SERIAL position in LIST
  46. ;;    Set SERIAL to 0 if no match
  47.     .XCREF    REGISTER_TO_SERIAL_FOR_LIST
  48. REGISTER_TO_SERIAL_FOR_LIST    MACRO    REGISTER, SERIAL, LIST
  49.     .XCREF    SERIAL_LIST_ITEM
  50. SERIAL_LIST_ITEM    =    0    ;; Initialize
  51. SERIAL    =    0            ;;  Assume no match
  52.     IRP    REGISTER_LIST_ITEM, <LIST>
  53. SERIAL_LIST_ITEM    =    SERIAL_LIST_ITEM + 1
  54.                     ;; Serial number for this list item
  55.     IFIDN    <REGISTER>, <REGISTER_LIST_ITEM>
  56. SERIAL    =    SERIAL_LIST_ITEM
  57.     EXITM
  58.     ENDIF                ;; IFIDN
  59.     ENDM                ;; IRP REGISTER_LIST_ITEM, <Upper case>
  60.     ENDM                ;; REGISTER_TO_SERIAL_FOR_LIST
  61. ;;
  62. ;;    Convert a REGISTER to a SERIAL position in the standard register list
  63. ;;    Set SERIAL to 0 if no match
  64.     .XCREF    REGISTER_TO_SERIAL
  65. REGISTER_TO_SERIAL    MACRO    REGISTER, SERIAL
  66.     REGISTER_TO_SERIAL_FOR_LIST    REGISTER, SERIAL, <AX,BX,CX,DX,SI,DI,SP,BP,DS,ES,SS>
  67.     IFE    SERIAL            ;; No upper case match
  68.     REGISTER_TO_SERIAL_FOR_LIST    REGISTER, SERIAL, <ax,bx,cx,dx,si,di,sp,bp,ds,es,ss>
  69.     ENDIF                ;; IFE SERIAL
  70.     ENDM                ;; REGISTER_TO_SERIAL
  71. ;;
  72. ;;    POP a register which matches SERIAL in the standard register list
  73. ;;        If unable to convert, enter
  74. ;;    "ERROR, BAD SPEC FOR SERIAL_TO_POP_REGISTER" as a syntax error
  75.     .XCREF SERIAL_TO_POP_REGISTER
  76. SERIAL_TO_POP_REGISTER    MACRO    SERIAL
  77.     .XCREF    UNMATCHED_SERIAL_TO_POP_REGISTER, SERIAL_LIST_ITEM
  78. SERIAL_LIST_ITEM    =    0    ;; Initialize
  79. UNMATCHED_SERIAL_TO_POP_REGISTER    =    1
  80.                     ;; Assume no match
  81.     IRP    REGISTER_LIST_ITEM, <AX,BX,CX,DX,SI,DI,SP,BP,DS,ES,SS>
  82. SERIAL_LIST_ITEM    =    SERIAL_LIST_ITEM + 1
  83.     IF    SERIAL EQ SERIAL_LIST_ITEM
  84.     POP    REGISTER_LIST_ITEM
  85. UNMATCHED_SERIAL_TO_POP_REGISTER    =    0
  86.                     ;; Show match
  87.     EXITM
  88.     ENDIF                ;; IF SERIAL EQ SERIAL_LIST_ITEM
  89.     ENDM                ;; IRP REGISTER_LIST_ITEM, <...>
  90.     IF    UNMATCHED_SERIAL_TO_POP_REGISTER
  91.     ERROR, BAD SPEC FOR SERIAL_TO_POP_REGISTER
  92.     ENDIF                ;; IF UNMATCHED_SERIAL_TO_POP_REGISTER
  93.     ENDM                ;; SERIAL_TO_POP_REGISTER
  94. ;;
  95.     .XCREF    PHASE_1_REGISTER_STACK_INIT, PHASE_2_REGISTER_STACK_INIT
  96. PHASE_1_REGISTER_STACK_INIT    =    1
  97. PHASE_2_REGISTER_STACK_INIT    =    1
  98. ;;
  99. ;;    Initial the register stack if necessary
  100.     .XCREF    REGISTER_STACK_INIT_IF_NECESSARY
  101. REGISTER_STACK_INIT_IF_NECESSARY    MACRO
  102.     .XCREF    REGISTER_STACK_TOP
  103.     IF1
  104.     IF PHASE_1_REGISTER_STACK_INIT    ;; Not initialized
  105. REGISTER_STACK_TOP    =    0
  106. PHASE_1_REGISTER_STACK_INIT    =    0
  107.     ENDIF                ;; IF PHASE_1_REGISTER_STACK_INIT
  108.     ENDIF                ;; IF1
  109.     IF2
  110.     IF PHASE_2_REGISTER_STACK_INIT    ;; Not re-initialized
  111. REGISTER_STACK_TOP    =    0
  112. PHASE_2_REGISTER_STACK_INIT    =    0
  113.     ENDIF                ;; IF PHASE_2_REGISTER_STACK_INIT
  114.     ENDIF                ;; IF2
  115.     ENDM                ;; REGISTER_STACK_INIT_IF_NECESSARY
  116. ;;
  117. ;;        Save VALUE in REGISTER_STACK(RS_TOP).  RS_TOP is always
  118. ;;    REGISTER_STACK_TOP, so this is really the end of PUSH_REGISTER_STACK
  119. ;;    which is necessary to create the name of REGISTER_STACK_TOP, because
  120. ;;    MASM only evaluates arguments.
  121.     .XCREF    SAVE_REGISTER_STACK
  122. SAVE_REGISTER_STACK    MACRO    RS_TOP, VALUE
  123.     .XCREF    REGISTER_STACK&RS_TOP
  124. REGISTER_STACK&RS_TOP    =    VALUE
  125.     ENDM                ;; SAVE_REGISTER_STACK
  126. ;;
  127. ;;    Push REGISTER_STACK_VALUE on the register stack
  128. ;;    Initialize the register stack if necessary.
  129.     .XCREF    PUSH_REGISTER_STACK
  130. PUSH_REGISTER_STACK    MACRO    REGISTER_STACK_VALUE
  131.     REGISTER_STACK_INIT_IF_NECESSARY
  132. REGISTER_STACK_TOP    =     REGISTER_STACK_TOP + 1
  133.     SAVE_REGISTER_STACK    %REGISTER_STACK_TOP, REGISTER_STACK_VALUE
  134.     ENDM                ;; PUSH_REGISTER_STACK
  135. ;;
  136. ;;    PUSH REGISTR
  137. ;;        Push the serial register index for REGISTR on the register 
  138. ;;    stack and return it in REGISTER_SERIAL.
  139. ;;        If an error, enter
  140. ;;    "ERROR, UNABLE TO PUSH REGISTER " and the register name as a
  141. ;;    syntax error and set REGISTER_SERIAL to 0.
  142.     .XCREF    PUSH_REGISTER_SERIAL
  143. PUSH_REGISTER_SERIAL    MACRO    REGISTR, REGISTER_SERIAL
  144.     REGISTER_TO_SERIAL    REGISTR, REGISTER_SERIAL
  145.                     ;; Find serial for standard register
  146.                     ;; list
  147.     IF    REGISTER_SERIAL        ;; No error
  148.     PUSH    REGISTR
  149.     PUSH_REGISTER_STACK    %REGISTER_SERIAL
  150.     ELSE                ;; Error
  151.     ERROR, UNABLE TO PUSH REGISTER REGISTR
  152.                     ;; Error message as syntax error
  153.     ENDIF                ;; IF REGISTER_SERIAL
  154.     ENDM                   ;; PUSH_REGISTER_SERIAL
  155. ;;
  156. ;;        POP the register whose serial number is in
  157. ;;    REGISTER_STACK(RS_TOP).  RS_TOP is always REGISTER_STACK_TOP, so
  158. ;;    this is really an intermediate step of POP_REGISTER_STACK, which
  159. ;;    is necessary to create the name of the variable and to get its
  160. ;;    value, because MASM only evaluate arguments.
  161.     .XCREF    REGISTER_STACK_TO_POP_REGISTER
  162. REGISTER_STACK_TO_POP_REGISTER    MACRO    RS_TOP
  163.     SERIAL_TO_POP_REGISTER    %(REGISTER_STACK&RS_TOP)
  164.     ENDM                ;; REGISTER_STACK_TO_POP_REGISTER
  165. ;;
  166. ;;        POP the register whose serial number is at the top of the
  167. ;;    register stack.
  168. ;;    Pop the register stack
  169. ;;        If the register stack is empty, enter
  170. ;;    "ERROR, UNABLE TO POP THE EMPTY REGISTER STACK" as a syntax error
  171.     .XCREF    POP_REGISTER_STACK
  172. POP_REGISTER_STACK    MACRO
  173.     IF    REGISTER_STACK_TOP    ;; Not empty
  174.     REGISTER_STACK_TO_POP_REGISTER    %REGISTER_STACK_TOP
  175. REGISTER_STACK_TOP    =    REGISTER_STACK_TOP - 1
  176.                     ;; Pop the register stack
  177.     ELSE                ;; Empty
  178.     ERROR, UNABLE TO POP THE EMPTY REGISTER STACK    
  179.     ENDIF                ;; IF REGISTER_STACK_TOP
  180.     ENDM                ;; POP_REGISTER_STACK
  181. ;;
  182.     .XCREF    PUSH_REGISTERS,POP_REGISTERS
  183. ;;
  184. ;;    Push the contents of a list of registers on the program's stack.
  185. ;;        Also set up a register stack in the macro language so that
  186. ;;    POP_REGISTERS will pop them in the proper reversed order.
  187. PUSH_REGISTERS    MACRO    REGS
  188. ;;        REGS is the list of registers.  The < and > are requred.
  189. ;;    Separate the registers with commas.
  190.     .XCREF    REGISTER_STACK_COUNT, REGISTER_SERIAL
  191. REGISTER_STACK_COUNT    =    0    ;; Initialize.  Will contain a
  192.                     ;; count of the registers pushed.
  193.                     ;; At the end, will be the top of
  194.                     ;; the register stack
  195.     IRP    REG,<REGS>
  196.     PUSH_REGISTER_SERIAL    REG, REGISTER_SERIAL
  197.                     ;; PUSH register REG on the program's
  198.                     ;; stack.  Push the serial number for
  199.                     ;; its name on the register stack
  200.     IF    REGISTER_SERIAL        ;; No errors
  201. REGISTER_STACK_COUNT    =    REGISTER_STACK_COUNT + 1
  202.     ENDIF                ;; IF REGISTER_SERIAL    
  203.     ENDM                ;; IRP REG,<REGS>
  204.     PUSH_REGISTER_STACK    %REGISTER_STACK_COUNT
  205.     ENDM                ;; PUSH_REGISTERS
  206. ;;
  207. ;;        Pop the register stack to REGISTER_STACK_COUNT.  RS_TOP is
  208. ;;    always REGISTER_STACK_TOP.  It is necessary because MASM only
  209. ;;    evaluates arguments
  210.     .XCREF    POP_REGISTER_STACK_COUNT
  211. POP_REGISTER_STACK_COUNT    MACRO    RS_TOP
  212. REGISTER_STACK_COUNT    =    REGISTER_STACK&RS_TOP
  213. REGISTER_STACK_TOP    =    REGISTER_STACK_TOP - 1
  214.     ENDM                ;; POP_REGISTER_STACK_COUNT
  215. ;;
  216. ;;    Pop the program's stack to the registers which PUSH_REGISTERS saved
  217. ;;        If there is nothing saved by PUSH_REGISTERS, enter
  218. ;;    "ERROR, NO REGISTERS LEFT TO POP" as a syntax error.
  219. POP_REGISTERS    MACRO
  220.     REGISTER_STACK_INIT_IF_NECESSARY
  221.     IF    REGISTER_STACK_TOP    ;; PUSH_REGISTERS has been run
  222.                     ;;    and not cleared
  223.     POP_REGISTER_STACK_COUNT    %REGISTER_STACK_TOP
  224.                     ;; Pop the register stack to
  225.                     ;; REGISTER_STACK_COUNT
  226.     IF    REGISTER_STACK_COUNT    ;; Some registers saved
  227.     REPT    REGISTER_STACK_COUNT
  228.     POP_REGISTER_STACK        ;; POP the proper register and pop
  229.                     ;; the register stack
  230.     ENDM                ;; REPT REGISTER_STACK_COUNT
  231.     ENDIF                ;; IF REGISTER_STACK_COUNT
  232.     ELSE                ;; PUSH_REGISTERS has not been run or has been
  233.                     ;; cleared
  234.     ERROR, NO REGISTERS LEFT TO POP
  235.     ENDIF                ;; IF REGISTER_STACK_TOP
  236.     ENDM                ;; POP_REGISTERS
  237. ;;
  238. ;;
  239. ;;        This ends the PUSH_REGISTERS - POP_REGISTERS material.  LP
  240. ;;
  241.     .XCREF    PUSHM,POPM
  242. ;;
  243. PUSHM    MACRO    REGS
  244.     IRP    REG,<REGS>
  245.     PUSH    REG
  246.     ENDM
  247.     ENDM
  248. ;;
  249. POPM    MACRO    REGS
  250.     IRP    REG,<REGS>
  251.     POP    REG
  252.     ENDM
  253.     ENDM
  254. ;;
  255.     .XCREF    GETZ,GETNZ,NOTZ
  256. ;;
  257. ;;        Set AL to TRUE if the ZERO flag is set.  Set it to FALSE
  258. ;;    if the ZERO flag is reset.  Don't change the ZERO flag.
  259. GETZ    MACRO
  260.     LOCAL    SKIPZ
  261.     MOV    AL,TRUE
  262.     JZ    SKIPZ
  263.     XOR    AL,AL
  264. SKIPZ:
  265.     AND    AL,AL
  266.     ENDM
  267. ;;        Set AL to TRUE if the ZERO flag is reset.  Set it to FALSE
  268. ;;    if the ZERO flag is set.  Don't change the ZERO flag.
  269. ;;
  270. GETNZ    MACRO
  271.     LOCAL    SKIPNZ
  272.     MOV    AL,TRUE
  273.     JNZ    SKIPNZ
  274.     XOR    AL,AL
  275. SKIPNZ:
  276.     ENDM
  277. ;;
  278. ;;        Reverse the ZERO flag.  As a side effect, set AH to 40H if
  279. ;;    the zero flag was set and to 0 if it was not.
  280. NOTZ    MACRO
  281.     LAHF
  282.     AND    AH,ZFLAG
  283.     ENDM
  284. ;;
  285.     .XCREF    JNNC,JNNZ,JNNNZ,JNNE,JNNNE,JNNS,JU,JNU,JNNCXZ
  286. ;;        All of the JNN? macros remove the "NN" which other macro
  287. ;;    substitutions can insert.  JU is an unconditional jump, and JNU
  288. ;;    is a NOP, which is the negation of an unconditional jump.
  289. JNNC    MACRO    LABEL
  290.     JC    LABEL
  291.     ENDM
  292. ;;
  293. JNNZ    MACRO    LABEL
  294.     JZ    LABEL
  295.     ENDM
  296. ;;
  297. JNNNZ    MACRO    LABEL
  298.     JNZ    LABEL
  299.     ENDM
  300. ;;
  301. JNNE    MACRO    LABEL
  302.     JE    LABEL
  303.     ENDM
  304. ;;
  305. JNNNE    MACRO    LABEL
  306.     JNE    LABEL
  307.     ENDM
  308. ;;
  309. JNNS    MACRO    LABEL
  310.     JS    LABEL
  311.     ENDM
  312. ;;
  313. JU    MACRO    LABEL
  314.     JMP    SHORT LABEL
  315.     ENDM
  316. ;;
  317. JNU    MACRO    LABEL            ;No code: "Jump never"!
  318.     ENDM
  319. ;;
  320. JNNCXZ    MACRO    LABEL
  321.     JCXZ    LABEL
  322.     ENDM
  323. ;;
  324.     .XCREF    JNNB,JNNA
  325. JNNB    MACRO    LABEL
  326.     JB    LABEL
  327.     ENDM
  328. ;;
  329. JNNA    MACRO    LABEL
  330.     JA    LABEL
  331.     ENDM
  332. ;;
  333. ;;
  334. ;;        Additional macros to eliminate the effects of JNN? by Lew
  335. ;;    Paper
  336.     .XCREF    JNNBE, JNNLE, JNNL, JNNGE, JNNG, JNNO, JNNP, JNPO, JNPE
  337. ;;
  338. JNNBE    MACRO    LABEL
  339.     JBE    LABEL
  340.     ENDM                ;; JNNBE
  341. ;;
  342. JNNLE    MACRO    LABEL
  343.     JLE    LABEL
  344.     ENDM                ;; JNNLE
  345. ;;
  346. JNNL    MACRO    LABEL
  347.     JL    LABEL
  348.     ENDM                ;; JNNL
  349. ;;
  350. JNNGE    MACRO    LABEL
  351.     JGE    LABEL
  352.     ENDM                ;; JNNGE
  353. ;;
  354. JNNG    MACRO    LABEL
  355.     JG    LABEL
  356.     ENDM                ;; JNNG
  357. JNNO    MACRO    LABEL
  358.     JO    LABEL
  359.     ENDM                ;; JNNO
  360. ;;
  361. JNNP    MACRO    LABEL
  362.     JP    LABEL
  363.     ENDM                ;; JNNP
  364. ;;
  365. JNPO    MACRO    LABEL
  366.     JPE    LABEL
  367.     ENDM                ;; JNPO
  368. ;;
  369. JNPE    MACRO    LABEL
  370.     JPO    LABEL
  371.     ENDM                ;; JNPE
  372. ;;
  373. ;;
  374. ;;        The macros in this section are by Lew Paper to automate
  375. ;;    the choices between LONG and SHORT backward jumps and to comment
  376. ;;    unncessary LONG forward jumps.
  377. ;;
  378. ;;
  379. ;;        Sets SHORT_LABEL to 1 if BACK_LABEL is within 126 bytes of $
  380. ;;    or to 0 if it is move than 126 bytes away.  Does not check that
  381. ;;    BACK_LABEL precedes $.
  382.     .XCREF    SET_SHORT_BACK
  383. SET_SHORT_BACK    MACRO    BACK_LABEL
  384.     .XCREF    SHORT_LABEL
  385. SHORT_LABEL    =    $ - OFFSET BACK_LABEL LE 126
  386.         ENDM            ;; SET_SHORT_BACK
  387. ;;
  388. ;;        Sets FORWARD_SHORT_LABEL to 1 if FORWARD_LABEL is within 
  389. ;;    MAX_BYTES bytes of $ or to 0 if it is move than MAX_BYTES bytes away.
  390. ;;     MAX_BYTES is 127 bytes maximum for all SHORT jumps plus 3 for the 
  391. ;;    size of a LONG jump which is added to a short jump around it for
  392. ;;    conditional jumps.  Does not check that FORWARD_LABEL follows $.
  393.     .XCREF    SET_SHORT_FORWARD
  394. SET_SHORT_FORWARD    MACRO    FORWARD_LABEL, MAX_BYTES
  395.     .XCREF    FORWARD_SHORT_LABEL
  396.     IF1                ;; Must define macro variables in
  397.                     ;; pass 1
  398. FORWARD_SHORT_LABEL    =    0    ;; Value is insignificant
  399.     ENDIF                ;; IF1
  400.     IF2                ;; FOWARD_LABEL is only defined on
  401.                     ;; pass 2
  402. FORWARD_SHORT_LABEL    =    OFFSET FORWARD_LABEL - $ LE MAX_BYTES
  403.     ENDIF                ;; IF2
  404.         ENDM            ;; SET_SHORT_FORWARD
  405. ;;
  406.     IFDEF    LP_DEBUG
  407.     .XCREF    D_SHORT_LABEL
  408. D_SHORT_LABEL    MACRO
  409.     .LALL
  410.     IF    SHORT_LABEL
  411. ; SHORT_LABEL is true
  412.     ELSE
  413. ; SHORT_LABEL is false
  414.     ENDIF                ;; IF SHORT_LABEL
  415.     SET_MACRO_EXPANSION
  416.     ENDM                ;; D_SHORT_LABEL
  417.     ENDIF                ;; IFDEF LP_DEBUG
  418. ;;
  419. ;;        Print a comment that a LONG jump to FORWARD_LABEL is not
  420. ;;    necessary if it isn't.  Set UNNECESSARY_LONG_COMMENTS to 1.  See
  421. ;;    SET_SHORT_FORWARD for a description of MAX_BYTES.
  422.     .XCREF    IS_LONG_NECESSARY
  423. IS_LONG_NECESSARY    MACRO    FORWARD_LABEL, MAX_BYTES
  424.     .XCREF    UNNECESSARY_LONG_COMMENTS
  425.     SET_SHORT_FORWARD    FORWARD_LABEL, MAX_BYTES
  426.                     ;; Set foward_short_label to TRUE
  427.                     ;; if LONG is not necessary
  428.     IF1                ;; Macro variables must be defined
  429.                     ;; in pass 1
  430. UNNECESSARY_LONG_COMMENTS    =    0
  431.                     ;; Value doesn't matter
  432.     ENDIF                ;; IF1
  433.     IF    FORWARD_SHORT_LABEL    ;; Can only be TRUE on pass 2
  434.     .LALL                ;; List this comment
  435. ;******    This jump doesn't need to be LONG ******
  436.     .SALL
  437.     SET_MACRO_EXPANSION
  438. UNNECESSARY_LONG_COMMENTS    =    1
  439.     ENDIF                ;; IF SHORT_LABEL
  440.     ENDM                ;; IS_LONG_NECESSARY
  441. ;;
  442. ;;    Write a warning of unncessary long comments
  443.     .XCREF    OUT_UNNECESSARY_LONG_COMMENTS
  444. OUT_UNNECESSARY_LONG_COMMENTS    MACRO
  445.     IFDEF    UNNECESSARY_LONG_COMMENTS
  446.     IF    UNNECESSARY_LONG_COMMENTS
  447.                     ;; Can only be positive on phase 2
  448.     .LALL
  449. ;
  450. ;    There were some LONG jumps which could have been SHORT.
  451. ;
  452.     .SALL
  453.     %OUT    *
  454.     %OUT    * There were some LONG jumps which could have been SHORT.
  455.     %OUT    *
  456.     ENDIF                ;; IF UNNECESSARY_LONG_COMMENTS
  457.     ENDIF                ;; IFDEF UNNECESSARY_LONG_COMMENTS
  458.     ENDM                ;; OUT_UNNECESSARY_LONG_COMMENTS
  459. ;;
  460. ;;        Set BACKWARD_JUMP to 1 if a jump to TARGET_LABEL is 
  461. ;;    backward or 0 if it is forward.
  462.     .XCREF    SET_BACKWARD_JUMP
  463. SET_BACKWARD_JUMP    MACRO TARGET_LABEL
  464.     .XCREF    BACKWARD_JUMP
  465.     IF1
  466.     IFNDEF    TARGET_LABEL        ;; Must be forward jump
  467. BACKWARD_JUMP    =    0
  468.     ENDIF                ;; IFNDEF TARGET_LABEL
  469.     IFDEF    TARGET_LABEL        ;; Must be backward jump
  470. BACKWARD_JUMP    =    1
  471.     ENDIF                ;; IFDEF TARGET_LABEL
  472.     ENDIF                ;; IF1
  473.     IF2
  474. BACKWARD_JUMP    =    $ - OFFSET TARGET_LABEL GT 0
  475.     ENDIF                ;; IF2
  476.     ENDM                ;; SET_BACKWARD_JUMP
  477. ;;
  478. ;;        Unconditional jump to TARGET_LABEL.  Automatically
  479. ;;    determine whether backward jump is LONG or SHORT.  Set some
  480. ;;    value in LONG to force a forward long jump.  If a forward
  481. ;;    jump is LONG but could be SHORT, write a remark to that effect
  482. ;;    and set UNNECESSARY_LONG_COMMENTS to 1.
  483.     .XCREF    LJMP
  484. LJMP    MACRO    TARGET_LABEL, LONG
  485.     LOCAL    BYPASS
  486.     SET_BACKWARD_JUMP TARGET_LABEL    ;; 1 if backward, 0 if forward
  487.     IF BACKWARD_JUMP
  488.     SET_SHORT_BACK    TARGET_LABEL    ;; SHORT_LABEL is TRUE if SHORT
  489.     ELSE                ;; Forward
  490.     .XCREF    SHORT_LABEL
  491. SHORT_LABEL    =    1        ;; Assume SHORT
  492.     IFNB    <LONG>            ;; LONG is not blank
  493.     .XCREF    LJMP_MAX_SHORT
  494. LJMP_MAX_SHORT    =    127 + OFFSET BYPASS - $
  495.     IS_LONG_NECESSARY    TARGET_LABEL, LJMP_MAX_SHORT
  496.                     ;; Write comment for unnecessary LONG
  497. SHORT_LABEL    =    0
  498.     ENDIF                ;; IFNB
  499.     ENDIF                ;; IF BACKWARD_JUMP
  500.     IF    SHORT_LABEL
  501.     JMP    SHORT TARGET_LABEL
  502.     ELSE                ;; LONG
  503.     JMP    TARGET_LABEL
  504.     ENDIF                ;; If short label
  505.     IFE    BACKWARD_JUMP        ;; Forward jump
  506.     .SALL
  507.     .XCREF    BYPASS
  508. BYPASS:
  509.     SET_MACRO_EXPANSION
  510.     ENDIF                ;; IFE BACKWARD_JUMP
  511.     ENDM                ;; LJMP
  512. ;;
  513. ;;        Conditional jump to TARGET_LABEL.  Automatically
  514. ;;    determine whether backward jump is LONG or SHORT.  Set some
  515. ;;    value in LONG to force a forward long jump.  If a forward
  516. ;;    jump is LONG but could be SHORT, write a remark to that effect
  517. ;;    and set UNNECESSARY_LONG_COMMENTS to 1.
  518. ;;        CONDITION is any completion to the J conditional jump prefix
  519. ;;    except CXZ, which deson't have a negation.
  520.     .XCREF    LCJMP
  521. LCJMP    MACRO    CONDITION, TARGET_LABEL, LONG
  522.     LOCAL    BYPASS
  523.     SET_BACKWARD_JUMP TARGET_LABEL    ;; 1 if backward, 0 if forward
  524.     IF BACKWARD_JUMP
  525.     SET_SHORT_BACK    TARGET_LABEL    ;; SHORT_LABEL is TRUE if SHORT
  526.     ELSE                ;; Forward
  527. SHORT_LABEL    =    1        ;; Assume SHORT
  528.     IFNB    <LONG>            ;; LONG is not blank
  529.     .XCREF    LCJMP_MAX_SHORT
  530. LCJMP_MAX_SHORT    =    127 + OFFSET BYPASS - $
  531.     IS_LONG_NECESSARY    TARGET_LABEL, LCJMP_MAX_SHORT
  532.                     ;; Write comment for unnecessary LONG
  533. SHORT_LABEL    =    0
  534.     ENDIF                ;; IFNB
  535.     ENDIF                ;; IF BACKWARD_JUMP
  536.     IF    SHORT_LABEL
  537.     J&CONDITION    TARGET_LABEL
  538.     ELSE                 ;; LONG
  539.     JN&CONDITION    BYPASS
  540.     JMP    TARGET_LABEL
  541.     ENDIF                ;; If short label
  542.     IFE    SHORT_LABEL AND BACKWARD_JUMP
  543.                     ;; Everything but short backward jump
  544.     IF    SHORT_LABEL        ;; Short forward jump
  545.     .XCREF    BYPASS
  546.     .SALL
  547.     ENDIF                ;; IF SHORT_LABEL
  548. BYPASS:
  549.     IF    SHORT_LABEL        ;; Short forward jump
  550.     SET_MACRO_EXPANSION
  551.     ENDIF                ;; IF SHORT_LABEL
  552.     ENDIF                ;; IFE SHORT_LABEL AND BACKWARD_JUMP
  553.     ENDM                ;; LCJMP
  554. ;;
  555. ;;        JCXZ to target label.  Automatically determine whether
  556. ;;    backward jump is LONG or SHORT.  Set some value in LONG to force
  557. ;;    a forward long jump.  If a forward jump is LONG but could be
  558. ;;    SHORT, write a remark to that effect and set
  559. ;;    UNNECESSARY_LONG_COMMENTS to 1.
  560.     .XCREF    LJCXZ
  561. LJCXZ    MACRO    TARGET_LABEL, LONG
  562.     LOCAL    BYPASS
  563.     SET_BACKWARD_JUMP TARGET_LABEL    ;; 1 if backward, 0 if forward
  564.     IF BACKWARD_JUMP
  565.     SET_SHORT_BACK    TARGET_LABEL    ;; SHORT_LABEL is TRUE if SHORT
  566.     ELSE                ;; Forward
  567. SHORT_LABEL    =    1        ;; Assume SHORT
  568.     IFNB    <LONG>            ;; LONG is not blank
  569.     .XCREF    LJXCZ_MAX_SHORT
  570. LJXCZ_MAX_SHORT    =    127 + OFFSET BYPASS - $
  571.     IS_LONG_NECESSARY    TARGET_LABEL, LJXCZ_MAX_SHORT
  572.                     ;; Write comment for unnecessary LONG
  573. SHORT_LABEL    =    0
  574.     ENDIF                ;; IFNB
  575.     ENDIF                ;; IF BACKWARD_JUMP
  576.     IF    SHORT_LABEL
  577.     JCXZ    TARGET_LABEL
  578.     ELSE                 ;; LONG
  579.     OR    CX,CX            ;; Is CX zero?
  580.     JNZ    BYPASS            ;; No.  Bypass long jump
  581.     JMP    TARGET_LABEL
  582.     ENDIF                ;; If short label
  583.     IFE    SHORT_LABEL AND BACKWARD_JUMP
  584.                     ;; Everything but short backward jump
  585.     IF    SHORT_LABEL        ;; Short forward jump
  586.     .XCREF    BYPASS
  587.     .SALL
  588.     ENDIF                ;; IF SHORT_LABEL
  589. BYPASS:
  590.     IF    SHORT_LABEL        ;; Short forward jump
  591.     SET_MACRO_EXPANSION
  592.     ENDIF                ;; IF SHORT_LABEL
  593.     ENDIF                ;; IFE SHORT_LABEL AND BACKWARD_JUMP
  594.     ENDM                ;; LJCXZ
  595. ;;
  596. ;;        Loop to TARGET_LABEL.  Automatically determine whether a
  597. ;;    backward jump is LONG or SHORT.  Set some value in LONG to force
  598. ;;    a forward long jump.  If a forward jump is LONG but could be SHORT,
  599. ;;    write a remark to that effect and set UNNECESSARY_LONG_COMMENTS to 1.
  600. ;;        CONDITION is blank for LOOP, E for LOOPE, Z for LOOPZ, 
  601. ;;    NZ for LOOPNZ or NE for LOOPNE.
  602.     .XCREF    LLOOP
  603. LLOOP    MACRO    CONDITION, TARGET_LABEL, LONG
  604.     LOCAL    BYPASS
  605.     SET_BACKWARD_JUMP TARGET_LABEL    ;; 1 if backward, 0 if forward
  606.     IF BACKWARD_JUMP
  607.     SET_SHORT_BACK    TARGET_LABEL    ;; SHORT_LABEL is TRUE if SHORT
  608.     ELSE                ;; Forward
  609. SHORT_LABEL    =    1        ;; Assume SHORT
  610.     IFNB    <LONG>            ;; LONG is not blank
  611.     .XCREF    LLOOP_MAX_SHORT
  612. LLOOP_MAX_SHORT    EQU    127 + OFFSET BYPASS - $
  613.     IS_LONG_NECESSARY    TARGET_LABEL, LLOOP_MAX_SHORT
  614.                     ;; Write comment for unnecessary LONG
  615. SHORT_LABEL    =    0
  616.     ENDIF                ;; IFNB
  617.     ENDIF                ;; IF BACKWARD_JUMP
  618.     IF    SHORT_LABEL
  619.     LOOP&CONDITION    TARGET_LABEL
  620.     ELSE                 ;; LONG
  621.     IFNB    <CONDITION>        ;; Don't test unconditional loop
  622.     JN&CONDITION    BYPASS        ;; Terminate loop if not CONDITION
  623.     ENDIF                ;; IFNB CONDITION
  624.     DEC    CX            ;; Does CX become zero
  625.     JZ    BYPASS            ;; Yes.  Terminate loop
  626.     JMP    TARGET_LABEL
  627.     ENDIF                ;; If short label
  628.     IFE    SHORT_LABEL AND BACKWARD_JUMP
  629.                     ;; Everything but short backward jump
  630.     IF    SHORT_LABEL        ;; Short forward jump
  631.     .XCREF    BYPASS
  632.     .SALL
  633.     ENDIF                ;; IF SHORT_LABEL
  634. BYPASS:
  635.     IF    SHORT_LABEL        ;; Short forward jump
  636.     SET_MACRO_EXPANSION
  637.     ENDIF                ;; IF SHORT_LABEL
  638.     ENDIF                ;; IFE SHORT_LABEL AND BACKWARD_JUMP
  639.     ENDM                ;; LLOOP
  640. ;;
  641. ;;
  642. ;;        From here on, the macros are more complicated.  They
  643. ;;    use automatic labels, whose names are sequentially indexed as
  644. ;;    L?1, L?2, ...   Macro variable L?CNT contains the last index used,
  645. ;;    and the next label will be L?&(L?CNT+1).  They also use a macro
  646. ;;    stack, whose members are indexec as S?1, S?2, ...  Each Sn contains a
  647. ;;    number which the macros use to construct a L?n label.  Macro
  648. ;;    variable S?CNT contains the S? index of the top of the macro stack,
  649. ;;    so a push will increment S?CNT, and a pop will decrement it.
  650. ;;
  651. ;;        Mark Hersey named the macro variables consistently as
  652. ;;    follows:
  653. ;;        ENTRY        The index of a macro stack variable.
  654. ;;        NUMBER        The index of a label or a macro stack
  655. ;;                variable.
  656. ;;        CONDITION    The condition part of a jump instruction,
  657. ;;                i.e., JZ would have CONDITION Z.
  658. ;;        LONG        This is always an optional variable.  If
  659. ;;                it exists, it forces a LONG jump of some
  660. ;;                sort.  If not, it forces a SHORT jump.
  661. ;;        VAR1        The required first variable of a compare.
  662. ;;        VAR2        The optional second variable of a compare.
  663. ;;                If it exists, it forces a CMP instruction.
  664. ;;                If it doesn't, the macro uses an
  665. ;;                AND VAR1,VAR1 instruction, which lets you
  666. ;;                test for zero or a negative variable.
  667.  
  668. ;;        To simplify the descriptions below, I will use the following
  669. ;;    terminology:
  670. ;;        L?N is a label with index N.
  671. ;;        CURRENT is L?CNT.
  672. ;;        NEXT is L?CNT+1.
  673. ;;        SECOND is L?CNT+2.
  674. ;;        S?N is the contents of macro stack variable with index N.
  675. ;;        stacktop is the contents of S?(S?CNT), the top of the stack.
  676. ;;        stacknext is (stacktop + 1).
  677. ;;
  678. ;;        Add 1 to contents of LABEL.  Written by LP, with
  679. ;;    modifications to later macros which had this operation.
  680. ;;
  681. ;;    Initialize the label and macro stack indices to 0.
  682.     .XCREF    XINIT
  683. XINIT    MACRO    
  684.     .XCREF    L?CNT,S?CNT
  685. L?CNT    =    0
  686. S?CNT    =    0
  687.     ENDM
  688. ;;
  689. ;;    Initialize the label and macro stack indices to 0 if necessary
  690.     .XCREF    PHASE_1_XINIT, PHASE_2_XINIT, XINIT_IF_NECESSARY
  691. PHASE_1_XINIT    =    1
  692. PHASE_2_XINIT    =    1
  693. XINIT_IF_NECESSARY    MACRO
  694.     IF1
  695.     IF PHASE_1_XINIT        ;; Not initialized
  696.     XINIT
  697. PHASE_1_XINIT    =    0
  698.     ENDIF                ;; IF PHASE_1_XINIT
  699.     ENDIF                ;; IF1
  700.     IF2
  701.     IF PHASE_2_XINIT        ;; Not re-initialized
  702.     XINIT
  703. PHASE_2_XINIT    =    0
  704.     ENDIF                ;; IF PHASE_2_XINIT
  705.     ENDIF                ;; IF2
  706.     ENDM                ;; XINIT_IF_NECESSARY
  707. ;;
  708.     .XCREF    XINC
  709. XINC    MACRO    LABEL
  710.     XINIT_IF_NECESSARY
  711. LABEL    =    LABEL+1
  712.     ENDM                ;; XINC
  713. ;;
  714.     .XCREF    XSAVE,XPUSH,XPOP
  715. ;;
  716. ;;        Set macro stack variable with index ENTRY to label index
  717. ;;    NUMBER.  In these macros, ENTRY is always S?CNT, and NUMBER is
  718. ;;    always L?CNT, so the macro becomes, "Set stacktop to the current
  719. ;;    label index."
  720. XSAVE    MACRO    ENTRY,NUMBER
  721.     .XCREF    S?&ENTRY
  722. S?&ENTRY=    NUMBER
  723.     ENDM
  724. ;;
  725. ;;    Push the next label index on the macro stack.
  726. ;;    Recall that XINC invokes XINIT_IF_NECESSARY.
  727. XPUSH    MACRO
  728.     XINC    L?CNT            ;; Set CURRENT to NEXT
  729.     XINC    S?CNT            ;; Set stacktop to stacknext
  730.     XSAVE    %S?CNT,%L?CNT
  731.     ENDM
  732. ;;
  733. ;;    Pop the macro stack.
  734. ;;    No automatic initialization to force an error if no XPUSH first.
  735. XPOP    MACRO
  736. S?CNT    =    S?CNT-1
  737.     ENDM
  738. ;;
  739.     .XCREF    XLBL,XLBLS,XLBLI
  740. ;;
  741. XLBL    MACRO    NUMBER
  742.     .XCREF    L?&NUMBER
  743. L?&NUMBER:
  744.     ENDM
  745. ;;
  746. ;;        Write the label L?(S?NUMBER):.  In these macros, NUMBER is
  747. ;;    always S?CNT, so the macro becomes, "Write the label L?stacktop."
  748. XLBLS    MACRO    NUMBER
  749.     XLBL    %S?&NUMBER
  750.     ENDM
  751. ;;
  752. ;;    Write the label L?(stacktop+1).
  753. XLBLI    MACRO    NUMBER
  754.     XINIT_IF_NECESSARY
  755.     XLBL    %(S?&NUMBER&+1)
  756.     ENDM
  757. ;;
  758.     .XCREF    XJMP,XJMPS,XJMPI
  759. ;;
  760. ;;    Jump to L?NUMBER.
  761. XJMP    MACRO    NUMBER,LONG
  762.     LJMP    L?&NUMBER, LONG
  763.     ENDM
  764. ;;
  765. ;;        Jump to L?(S?NUMBER).
  766. XJMPS    MACRO    NUMBER,LONG
  767.     XJMP    %S?&NUMBER,LONG
  768.     ENDM
  769. ;;
  770. ;;        Jump to L?(S?NUMBER+1).
  771. XJMPI    MACRO    NUMBER,LONG
  772.     XJMP    %(S?&NUMBER+1),LONG
  773.     ENDM
  774. ;;
  775.     .XCREF    XLP,XLPS
  776. ;;
  777. ;;    LOOP for CONDITION to L?NUMBER.
  778. XLP    MACRO    CONDITION,NUMBER,LONG
  779.     LLOOP    CONDITION,L?&NUMBER,LONG
  780.     ENDM
  781. ;;
  782. ;;        LOOP for CONDITION to L?(S?NUMBER).  In these macros,
  783. ;;    S?NUMBER is always S?CNT, so the macro becomes, "LOOP for CONDITION
  784. ;;    to L?stacktop."
  785. XLPS    MACRO    CONDITION,NUMBER,LONG
  786.     XLP    CONDITION,%S?&NUMBER,LONG
  787.     ENDM
  788. ;;
  789.     .XCREF    XCJMP,XCJMPS,XCJMPI,XCXZJMP
  790. ;;
  791. ;;    Jump on condition to L?NUMBER.
  792. XCJMP    MACRO    CONDITION,NUMBER,LONG
  793.     LCJMP    CONDITION,L?&NUMBER,LONG
  794.     ENDM
  795. ;;
  796. ;;        Jump on CONDITION to L?(S?NUMBER).
  797. XCJMPS    MACRO    CONDITION,NUMBER,LONG
  798.     XCJMP    CONDITION,%S?&NUMBER,LONG
  799.     ENDM
  800. ;;
  801. ;;        Jump on CONDITION to L?(S?NUMBER+1).
  802. XCJMPI    MACRO    CONDITION,NUMBER,LONG
  803.     XINIT_IF_NECESSARY
  804.     XCJMP    CONDITION,%(S?&NUMBER+1),LONG
  805.     ENDM
  806. ;;
  807. ;;    Jump if CX = 0 to label L?NUMBER.
  808. XCXZJMP    MACRO    NUMBER,LONG
  809.     LJCXZ    L?&NUMBER,LONG
  810.     ENDM
  811. ;;
  812.     .XCREF    XIFC,XANDIFC,COMPARE,XIF,XANDIF,XELSE,XENDIF
  813. ;;
  814. ;;    Push the next label index on the macro stack.
  815. ;;    Jump if CONDITION is false to L?stacktop.
  816. ;;    NOTE that the jump is apparently backward to allow for ANDing
  817. ;;    conditions with XANDIFC.
  818. XIFC    MACRO    CONDITION,LONG
  819.     XPUSH
  820.     XCJMP    N&CONDITION,%L?CNT,LONG
  821.     ENDM
  822. ;;
  823. ;;    Jump if CONDITION is false to L?stacktop.
  824. ;;    Use only after invoking XIFC to set up the macro stack.
  825. XANDIFC    MACRO    CONDITION,LONG
  826.     XCJMP    N&CONDITION,%L?CNT,LONG
  827.     ENDM
  828. ;;
  829. ;;    Compare VAL1 to either itself or VAL2.
  830. COMPARE    MACRO    VAL1,VAL2
  831.     IFNB    <VAL2>
  832.     CMP    VAL1,VAL2
  833.     ENDIF
  834.     IFB    <VAL2>
  835.     AND    VAL1,VAL1
  836.     ENDIF
  837.     ENDM
  838. ;;
  839. ;;    Compare VAL1 to either itself or VAL2.
  840. ;;    Jump if CONDITION is false to L?stacktop.
  841. ;;    NOTE that the jump is apparently backward to allow for ANDing
  842. ;;    conditions with XANDIFC.
  843. XIF    MACRO    VAL1,CONDITION,VAL2,LONG
  844.     COMPARE    <VAL1>,<VAL2>
  845.     XIFC    CONDITION,LONG
  846.     ENDM
  847. ;;
  848. ;;    Compare VAL1 to either itself or VAL2.
  849. ;;    Jump if CONDITION is false to L?stacktop.
  850. ;;    Use only after invoking XIFC to set up the macro stack.
  851. XANDIF    MACRO    VAL1,CONDITION,VAL2,LONG
  852.     COMPARE    <VAL1>,<VAL2>
  853.     XANDIFC    CONDITION,LONG
  854.     ENDM
  855. ;;
  856. ;;    Increment CURRENT and jump to L?CURRENT.
  857. ;;    Write a l?stacktop here as a label
  858. ;;    Replace stacktop with CURRENT.
  859. XELSE    MACRO    LONG
  860.     XINC    L?CNT            ;; Set CURRENT to NEXT
  861.     XJMP    %L?CNT,LONG
  862.     XLBLS    %S?CNT
  863.     XSAVE    %S?CNT,%L?CNT
  864.     ENDM
  865. ;;
  866. ;;    Write L?stacktop here as a label.
  867. ;;    Pop the macro stack.
  868. XENDIF    MACRO
  869.     XLBLS    %S?CNT
  870.     XPOP
  871.     ENDM
  872. ;;
  873.     .XCREF    XLOOP,XENDLP,XBEGIN,XEND,XNEXT,XCNEXTC,XCNEXT,XCEXITC,XEXIT
  874.     .XCREF    XCEXIT,XWHILEC,XWHILE,XREPEAT,XUNTILC,XUNTIL,XFOR,XDEC
  875. ;;
  876. ;;    Increment CURRENT and push it on the macro stack
  877. ;;    Write L?CURRENT here as a label
  878. ;;    Increment CURRENT again, so that it is the original SECOND
  879. XLOOP    MACRO
  880.     XPUSH
  881.     XLBL    %L?CNT
  882.     XINC    L?CNT            ;; Set CURRENT to NEXT
  883.     ENDM
  884. ;;
  885. ;;    Increment CURRENT and push it on the macro stack
  886. ;;    Write L?CURRENT as a label.
  887. XBEGIN    MACRO
  888.     XPUSH
  889.     XLBL    %L?CNT
  890.     XINC    L?CNT            ;; Set CURRENT to NEXT
  891.     ENDM
  892. ;;
  893. ;;    Jump on CONDITION to stacktop
  894. XNEXT    MACRO    LONG
  895.     XJMPS    %S?CNT,LONG
  896.     ENDM
  897. ;;
  898. ;;    Jump on CONDITION to stacktop
  899. XCNEXTC    MACRO    CONDITION,LONG
  900.     XCJMPS    CONDITION,%S?CNT,LONG
  901.     ENDM
  902. ;;
  903. ;;    Compare VAL1 to VAL2 or itself
  904. ;;    Jump on CONDITION to stacktop
  905. XCNEXT    MACRO    VAL1,CONDITION,VAL2,LONG
  906.     COMPARE    <VAL1>,<VAL2>
  907.     XCNEXTC    CONDITION,LONG
  908.     ENDM
  909. ;;
  910. ;;    Jump to stacknext.
  911. XEXIT    MACRO    LONG
  912.     XJMPI    %S?CNT,LONG
  913.     ENDM
  914. ;;
  915. ;;    Jump on condition to stacknext.
  916. XCEXITC    MACRO    CONDITION,LONG
  917.     XCJMPI    CONDITION,%S?CNT,LONG
  918.     ENDM
  919. ;;
  920. ;;    Compare VAL1 with either itself or VAL2
  921. ;;    Jump on condition to stacknext
  922. XCEXIT    MACRO    VAL1,CONDITION,VAL2,LONG
  923.     COMPARE    <VAL1>,<VAL2>
  924.     XCEXITC    CONDITION,LONG
  925.     ENDM
  926. ;;
  927. ;;    Jump to stacktop
  928. ;;    Write the label L?stacknext
  929. XENDLP    MACRO    LONG
  930.     XJMPS    %S?CNT,LONG
  931.     XLBLI    %S?CNT
  932.     XPOP
  933.     ENDM
  934. ;;
  935. ;;    Write the label L?stacknext
  936. ;;    Pop the macro stack
  937. XEND    MACRO
  938.     XLBLI    %S?CNT
  939.     XPOP
  940.     ENDM
  941. ;;
  942. ;;    Increment CURRENT and push it on the macro stack
  943. ;;    Write L?CURRENT here as a label
  944. ;;    Increment CURRENT again, so that it is the original SECOND
  945. ;;    Jump on CONDITION FALSE to stacknext, which is now CURRENT
  946. XWHILEC    MACRO    CONDITION,LONG
  947.     XLOOP
  948.     XCEXITC    N&CONDITION,LONG
  949.     ENDM
  950. ;;
  951. ;;    Increment CURRENT and push it on the stack
  952. ;;    Write L?CURRENT here as a label
  953. ;;    Increment CURRENT again, so that it is the original SECOND
  954. ;;    Compare VAL1 with either itself or VAL2
  955. ;;    Jump on CONDITION FALSE to stacknext, which is now CURRENT
  956. XWHILE    MACRO    VAL1,CONDITION,VAL2,LONG
  957.     XLOOP
  958.     XCEXIT    <VAL1>,N&CONDITION,<VAL2>,LONG
  959.     ENDM
  960. ;;
  961. ;;    Increment CURRENT and push it on the stack
  962. ;;    Write L?CURRENT here as a label
  963. ;;    Increment CURRENT again, so that it is the original SECOND
  964. XREPEAT    MACRO
  965.     XLOOP
  966.     ENDM
  967. ;;
  968. ;;    Jump on CONDITION FALSE to stacknext, which is now CURRENT
  969. ;;    Write L?NEXT here as a label
  970. ;;    Pop the macro stack
  971. XUNTILC    MACRO    CONDITION
  972.     XCJMPS    N&CONDITION,%S?CNT
  973.     XLBLI    %S?CNT
  974.     XPOP
  975.     ENDM
  976. ;;
  977. ;;    Compare VAL1 with either itself or VAL2
  978. ;;    Jump on CONDITION FALSE to stacknext, which is now CURRENT
  979. ;;    Write L?NEXT here as a label
  980. ;;    Pop the macro stack
  981. XUNTIL    MACRO    VAL1,CONDITION,VAL2
  982.     COMPARE    <VAL1>,<VAL2>
  983.     XUNTILC    CONDITION
  984.     ENDM
  985. ;;
  986. ;;    If no arguments, forward jump to L?SECOND
  987. ;;    Increment L?CNT and push it
  988. ;;    Write L?CURRENT here as a label
  989. ;;    Increment L?CURREMT again, so it is now the original SECOND
  990. XFOR    MACRO    NOZERO,LONG
  991. ;;    NOZERO's value doesn't matter.  Any value will surpress the forward
  992. ;;          jump.
  993.     IFB    <NOZERO>
  994.     XINIT_IF_NECESSARY
  995.     XCXZJMP    %(L?CNT+2),LONG
  996.     ENDIF
  997.     XLOOP
  998.     ENDM
  999. ;;
  1000. ;;        LOOP on CONDITION to L?stacktop
  1001. ;;    Write the label L?(stacktop+1).
  1002. ;;    POP the macro stack
  1003. XDEC    MACRO    CONDITION,LONG
  1004.     XLPS    CONDITION,%S?CNT,LONG
  1005.     XLBLI    %S?CNT
  1006.     XPOP
  1007.     ENDM
  1008. ;;
  1009.     .XCREF    CALLSTR,FCBCALL,GETCALL
  1010. ;;
  1011. ;;        In the next three macros,
  1012. ;;    ROUTINE is the routine name.
  1013. ;;    STRING is the name of the variable which BX or DX is to point to.
  1014. ;;
  1015. ;;    Call a routine which uses an offset in DX
  1016. CALLSTR    MACRO    ROUTINE,STRING
  1017.     MOV    DX,OFFSET STRING
  1018.     CALL    ROUTINE
  1019.     ENDM
  1020. ;;
  1021. ;;    Call a routine which uses an offset in BX
  1022. GETCALL   MACRO     ROUTINE,STRING
  1023.      MOV  BX,OFFSET STRING
  1024.      CALL ROUTINE
  1025.      ENDM
  1026. ;;
  1027. ;;    Call a routine which calls a DOS file control block function.
  1028. ;;    The routine should reset the zero flag if it succeeds and set
  1029. ;;    the zero flag for failure.
  1030. ;;    
  1031. FCBCALL    MACRO    ROUTINE,FCB,ERRORRTN
  1032. ;;    ERRORRTN is an optional label for an error routine.
  1033.     MOV    DX,OFFSET FCB
  1034.     CALL    ROUTINE
  1035.     IFNB    <ERRORRTN>
  1036.                     JZ    ERRORRTN
  1037.     ENDIF
  1038.     ENDM
  1039. ;;
  1040.     .CREF
  1041.     SET_MACRO_EXPANSION
  1042. ;;
  1043.  
  1044.